"""
指数择时实盘 | 邢不行 | 2023分享会
author: 邢不行
微信: xbx6660
"""
import os
import json
import base64
import hashlib
import traceback
from datetime import datetime
import requests
from config import proxy, wechat_webhook_url
import dataframe_image as dfi
"""
dataframe_image.export参数简要说明

table_conversion默认是chrome，需要安装chrome，安装麻烦，速度又慢，偶尔卡死进程

table_conversion='matplotlib'，速度快，中文需要修改源码等处理，用英文吧

如果df超过100行会报错，设置 max_rows = -1

如果df超过30列会报错，设置 max_cols = -1

存在计算机生成图片崩溃的可能，主要df别太大
"""


# 企业微信通知

def send_wechat_work_msg(content):
    """
    企业微信发送通知给机器人

    :param content:    发送的消息内容
    """
    try:
        # 构建企业微信机器人的消息体。参考文档：https://developer.work.weixin.qq.com/document/path/91770
        data = {
            "msgtype": "text",
            "text": {
                "content": content + '\n' + datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        }
        # 使用requests发送通知
        r = requests.post(wechat_webhook_url, data=json.dumps(data), timeout=10, proxies=proxy)
        print(f'调用企业微信接口返回： {r.text}')
        print('成功发送企业微信')
    except Exception as e:
        print(f"发送企业微信失败:{e}")
        print(traceback.format_exc())


# 上传图片，解析bytes
class MyEncoder(json.JSONEncoder):
    """
    定义一个JSON编码器，用来处理默认json无法转换的数据类型，例如：图片
    """

    def default(self, obj):
        """
        只要检查到了是bytes类型的数据就把它转为str类型
        :param obj:
        :return:
        """
        if isinstance(obj, bytes):
            return str(obj, encoding='utf-8')
        return json.JSONEncoder.default(self, obj)


# 企业微信发送图片
def send_wechat_work_img(file_path):
    """
    企业微信发送图片
    :param file_path:       本地图片的位置
    :return:
    """
    try:
        # 判断图片是否存在
        if not os.path.exists(file_path):  # 图片不存在，跳过该程序
            print('找不到图片')
            return
        # 读取图片文件
        with open(file_path, 'rb') as f:
            image_content = f.read()
        image_base64 = base64.b64encode(image_content).decode('utf-8')  # 将图片进行编码，转成base64格式
        md5 = hashlib.md5()  # 构建md5
        md5.update(image_content)  # 对于base64格式的图片进行md5加密
        image_md5 = md5.hexdigest()  # 获取图片加密后的hex码，这就是常见的md5码
        data = {
            'msgtype': 'image',
            'image': {
                'base64': image_base64,
                'md5': image_md5
            }
        }
        # 服务器上传bytes图片的时候，json.dumps解析会出错，需要自己手动去转一下
        r = requests.post(wechat_webhook_url, data=json.dumps(data, cls=MyEncoder, indent=4), timeout=10, proxies=proxy)
        print(f'调用企业微信接口返回： {r.text}')
        print('成功发送企业微信')
    except Exception as e:
        print(f"发送企业微信失败:{e}")
        print(traceback.format_exc())
    finally:
        # 判断图片是否存在
        if os.path.exists(file_path):  # 不管有没有发送成功，最后都去删除图片
            os.remove(file_path)


def send_msg_for_position(account_config):
    """
    对于成功获取净值和持仓信息的账户，发送信息给机器人

    :param: account_config:      账户列表
    """
    msg = ''
    for _account in account_config:
        # 获取账户的配置
        equity = account_config[_account]['equity']  # 获取账户净值（不包含未实现盈亏）
        position_df = account_config[_account]['position_df']  # 获取账户持仓

        # 判断当前账户是否持仓
        if not position_df.empty:  # 当前有持仓
            try:
                pos_pic_path = os.path.join(data_path, 'pos.png')  # 定义持仓的图片路径
                # 持仓数据转换(中文字段更换英文)
                send_df = position_df.copy()  # 防止修改数据，影响其他函数的运行结果
                send_df['pct_change'] = send_df['当前标记价格'] / send_df['均价'] - 1  # 计算开仓后涨跌幅
                send_df['pct_change'] = send_df['pct_change'].transform(lambda x: f'{x * 100:.2f}%')  # 最后将数据转成百分比
                send_df['pos_u'] = send_df['当前持仓量'] * send_df['当前标记价格']  # 计算持仓价值
                send_df['account'] = _account  # 标识哪一个账号
                send_df.rename(columns={'持仓方向': 'side', '持仓盈亏': 'pnl_u'}, inplace=True)  # 将部分中文重命名为英文
                send_df = send_df[['side', 'pct_change', 'pos_u', 'pnl_u', 'account']]  # 只保留指定字段

                dfi.export(send_df, pos_pic_path, table_conversion='matplotlib')  # 导出图片
                send_wechat_work_img(pos_pic_path)  # 发送图片
            except BaseException as e:
                print(traceback.format_exc())
                print('持仓数据转换图片出现错误')

            # 账户净值信息追加未实现盈亏数据
            msg += f'账户： {_account}\n'
            msg += f'账户净值： {(equity + position_df["持仓盈亏"].sum()):8.2f}\n'
            msg += f'当前持仓盈亏： {(position_df["持仓盈亏"].sum()):8.2f}\n'
        else:  # 当前没有持仓
            # 直接发送账户净值信息
            msg += f'账户： {_account}\n'
            msg += f'账户净值： {equity:8.2f}\n'

        msg += f'=' * 20 + '\n\n'

    # 判断发送信息内容是否为空
    if msg:  # 发送信息内容不为空
        send_wechat_work_msg(msg)


def send_msg_for_order(order_param, order_res):
    """
    发送下单信息

    :param: order_param:    下单参数
    :param: order_res:      交易所返回的下单后信息
    """
    msg = ''
    for _ in range(len(order_param)):
        msg += f'币种:{order_param[_]["symbol"]}\n'
        msg += f'方向:{"做多" if order_param[_]["side"] == "BUY" else "做空"}\n'
        msg += f'价格:{order_param[_]["price"]}\n'
        msg += f'数量:{order_param[_]["quantity"]}\n'

        # 判断'msg'是否存在order_res中
        if 'msg' in order_res[_].keys():  # 若'msg'字段存在，表示下单没有成功，追加下单失败的信息'msg'
            msg += f'下单结果:{order_res[_]["msg"]}'
        else:  # 若'msg'字段不存在，标识下单成功
            msg += f'下单结果: 下单成功'

        msg += '\n' * 2

    # 发送信息内容
    send_wechat_work_msg(msg)
    
def send_dataframe(dataframe):
    try:
        df_str=dataframe.to_string(index=True)
        message = {
        "msgtype": "text",
        "text": {
            "content": df_str + '\n' + datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }}
            # 使用requests发送通知
        r = requests.post(wechat_webhook_url, data=json.dumps(message), timeout=10, proxies=proxy)
        print(f'调用企业微信接口返回： {r.text}')
        print('成功发送企业微信')

    except Exception as e:
        print(f"发送企业微信失败:{e}")
        print(traceback.format_exc())
    